import { jobGoto } from "../../../job/goto";
import { jobSpread } from "../../../job/spread";
import { jobTransfer } from "../../../job/transfer";
import { getApproprateEnergy, getCreepCountCoeficient, getMinEnergy, spawnCreep } from "../../../role/spawn";
import { posMid } from "../../../util/position";
import { getActualFreeCapacity, getActualResourceAmount, getFreeCapacity, transportResource } from "../../../util/store";
import { GroupWork } from "./work";

export class GroupHarvesters extends GroupWork{

    /**
     * 
     * @param {Room} spawnRoom 
     * @param {Source | string} source 
     */
    constructor(spawnRoom=undefined, source=undefined){
        super(spawnRoom, source);
        if(source!=undefined){
            this.init(source);
        }
    }
    init(source){
        if(source instanceof Source){
            this.sourceId=source.id;
            this.targetRoomName=source.room.name;
        }else if(typeof source === 'string'){
            this.sourceId=source;
            if(this.source){
                this.targetRoom = this.source.room;
            }
        }else{
            throw new Error('unknown source type');
        }
    }
    /**
     * @returns {Source|null}
     */
    get source(){
        return Game.getObjectById(this.sourceId);
    }
    isSourceReserved(){
        if(!this.targetRoom){
            return false;
        }
        if(!this.targetRoom.controller){
            return true;
        }
        let reservation = this.targetRoom.controller.reservation;
        return this.targetRoom.controller.my || 
            (reservation && reservation.ticksToEnd>100 && reservation.username === this.spawnRoom.controller.owner.username)
    }
    set source(source){
        if(source==undefined){
            this.sourceId=undefined;
            return;
        }
        this.sourceId=source.id;
    }
    get size(){
        let ans = 0;
        for(let creep of this.creeps){
            if(creep.ticksToLive>creep.body.length*2 || creep.spawning){
                ans++;
            }
        }
        return ans;
    }
    /**
     * 
     * @param {Room} room 
     * @returns {Number}
     */
    static getGroupMaxSize(room){
        return room.controller.level>=3?1:2;
    }
    get maxSize(){
        return GroupHarvesters.getGroupMaxSize(this.spawnRoom);
    }
    get targetRoom(){
        if(!this.targetRoomName){
            return undefined;
        }
        return Game.rooms[this.targetRoomName];
    }
    set targetRoom(targetRoom){
        if(!(targetRoom instanceof Room)){
            return;
        }
        this.targetRoomName=targetRoom.name;
    }
    serialize(){
        return {
            super:super.serialize(), 
            source:this.sourceId, 
            targetRoomName:this.targetRoomName,
            linkId:this.linkId,
        };
    }
    deserialize(obj){
        if(obj===undefined){
            return this;
        }
        super.deserialize(obj.super);
        this.sourceId=obj.source;
        this.targetRoomName = obj.targetRoomName;
        this.linkId = obj.linkId;

        return this;
    }

    spawn(totalSize = Infinity){
        if(this.size>=this.maxSize){
            return ERR_INVALID_TARGET;
        }
        let energy = totalSize < 2 ? getMinEnergy(this.spawnRoom) : getApproprateEnergy(this.spawnRoom);
        let template = {
            require: [WORK, MOVE, CARRY],
            move: 1/6,
            work: 1/3,
            carry: 1/2,
        }
        if(energy>=1200){
            template = {
                require:[
                    WORK,WORK,WORK,WORK,WORK,WORK,
                    MOVE,MOVE,MOVE,
                ],
                carry: 1.00,
            }
        }
        let newCreep=spawnCreep(this.spawnRoom, template,{
            memory:{
                role: 'harvester',
            }
        },energy);
        if(!(newCreep instanceof Creep)){
            return newCreep;
        }
        this.addCreep(newCreep);
        return OK;
    }

    get link(){
        if(!this.linkId){
            return undefined;
        }
        return Game.structures[this.linkId];
    }

    run(forceStay = false, targetLink = undefined){
        if(Game.time%9==7 && this.source && this.targetRoom && !(this.link instanceof StructureLink)){
            let creeps = this.creeps;
            let source = this.source;
            let links = this.targetRoom.find(FIND_MY_STRUCTURES, {
                filter(structure){
                    if(structure.structureType !== STRUCTURE_LINK){
                        return false;
                    }
                    for(let creep of creeps){
                        if(!creep.pos.isNearTo(source.pos)){
                            continue;
                        }
                        if(creep.pos.isNearTo(structure)){
                            return true;
                        }
                    }
                    return false;
                }
            });
            if(links.length>0){
                this.linkId = links[0].id;
            }
        }
        if((targetLink instanceof StructureLink) && (this.link instanceof StructureLink)){
            let is_full = getActualFreeCapacity(this.link) === 0;
            let is_target_empty = getActualResourceAmount(targetLink) === 0;
            if(is_full || is_target_empty){
                transportResource(this.link, targetLink);
            }
        }
        if(this.source instanceof Source){
            this.targetRoomName = this.source.room.name;
        }
        return super.run(forceStay, targetLink);
    }

    /**
     * 
     * @param {Creep} creep 
     * @param {boolean} forceStay
     * @returns {CreepActionReturnCode}
     */
    creepRun(creep, forceStay=false, targetLink = undefined){
        if(this.sourceId===undefined){
            return ERR_INVALID_ARGS;
        }
        let ret = OK;
        if(creep.memory.working){
            let targets = jobTransfer.getTargets(this.spawnRoom)
                .filter((target)=>!(target instanceof StructureTower));
            ret = jobTransfer.run(creep, {visualizePathStyle:{stroke:"#ffffff"}}, targets);
                
            creep.say('work:'+ret);
            if(creep.store[RESOURCE_ENERGY] === 0){
                creep.memory.working=false;
                return OK;
            }
        }else{
            if(forceStay && getActualFreeCapacity(creep) === 0){
                return OK;
            }
            let target=this.source;
            if(!(target instanceof Source)){
                if(this.targetRoomName){
                    target = {
                        pos: new RoomPosition(24,24,this.targetRoomName),
                    };
                    return jobGoto.run(
                        creep, 
                        [target],
                        {visualizePathStyle:{stroke:"#ffff00"}}
                    );
                }
                return ERR_INVALID_TARGET;
            }
            ret = creep.harvest(target);
            if(ret === ERR_NOT_IN_RANGE || ret === ERR_NOT_OWNER){
                ret = jobGoto.run(
                    creep, 
                    [target],
                    {visualizePathStyle:{stroke:"#ffff00"}}
                );
            }else if((targetLink instanceof StructureLink) && (this.link instanceof StructureLink) && getFreeCapacity(targetLink) > 0){
                let ret = transportResource(creep, this.link);
                if(ret === ERR_NOT_IN_RANGE){
                    creep.moveTo(posMid(this.link.pos, this.source.pos));
                }else if(ret === ERR_NO_PATH){
                    jobSpread.run(creep);
                }
            }
            let sources = creep.pos.lookFor(LOOK_RESOURCES);
            if(sources.length>0){
                creep.pickup(sources[0]);
            }
            creep.say('harvest:'+ret);
            if(creep.store.getFreeCapacity() === 0){
                if(!forceStay){
                    creep.memory.working=true;
                }
                return OK;
            }
        }

        return ret;
    }
}
